﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ContainerVariations.Common;
using NUnit.Framework;

namespace ContainerVariations
{
    [TestFixture]
    public abstract class BaseTests
    {
        /// <summary>
        /// To pass this test, the container must be configured to resolve
        /// the same instance of the component for all resolution calls. In 
        /// some respects this is like a singleton (though it is not truly
        /// global and the class doesn't need all that nasty static plumbing
        /// which complicates testing). This is the default behavior for
        /// some, but not all, containers.
        /// </summary>
        public abstract void Configure_a_single_object_with_container_lifetime();

        /// <summary>
        /// To pass this test, the container must be configured to resolve
        /// all calls for the requested abstraction with a new instance of the
        /// configured component.  This is the default behavior for some,
        /// but not all containers.
        /// </summary>
        public abstract void Configure_a_single_object_with_transient_lifetime();

        /// <summary>
        /// To pass this test, the container must be configured to resolve
        /// all the dependencies for the requested abstraction as they are
        /// explicitly specified in the configuration. This is generally the
        /// hard way to do things.
        /// </summary>
        public abstract void Configure_a_manually_wired_dependency_graph();

        /// <summary>
        /// To pass this test, the container must be configured to 
        /// automatically resolve all the dependencies for the requested
        /// abstraction from the various implementations available in its 
        /// configuration. This is commonly referred to as 'autowiring' and
        /// one of the things that makes IoC containers so handy.
        /// </summary>
        public abstract void Configure_an_autowired_dependency_graph();

        /// <summary>
        /// To pass this test, the container must be configured via XML to 
        /// automatically resolve all the dependencies for the requested
        /// abstraction from the various implementations available in its 
        /// configuration.
        /// </summary>
        public abstract void Xml_configure_an_autowired_dependency_graph();

        public abstract void Configure_an_open_generic();

        public abstract void Configure_a_type_with_a_lambda_or_delegate_dependency();

        public abstract void Configure_all_classes_of_a_specified_type_in_a_specified_assembly();

        public abstract void Configure_a_class_with_multiple_decorators();

        public abstract void Configure_a_class_with_scalar_dependencies_from_configuration_api();

        public abstract void Container_can_use_an_instance_created_outside_the_container();

        public abstract void Disposable_classes_with_container_lifetime_are_disposed_when_container_is_disposed();

        public abstract void Disposable_classes_with_transient_lifetime_are_disposed_when_container_is_disposed();

        /// <summary>
        /// To pass this test, the container must be configured dynamically, 
        /// and then have additional XML configuration applied. The scenario 
        /// is on of routine configuration accomplished via dynamic 
        /// configuration with XML configuration used to make client or
        /// deployment specific implementation changes.
        /// </summary>
        public virtual void Container_can_use_combined_dynamic_and_xml_configuration()
        {
            
        }

        /// <summary>
        /// To pass this test the container must be configured to use a 
        /// specific ctor with no arguments and be configured to use a
        /// specific ctor with many arguments.  Some containers default to 
        /// the ctor with the most arguments it can provide (Castle).
        /// Others default tot he least arguments (NInject I believe,
        /// but don't quote me on that).  Can this container be made to
        /// do either?
        /// </summary>
        public virtual void Container_can_be_configured_to_use_a_specific_ctor_overload()
        {
            
        }

        /// <summary>
        /// To pass this test the container must permit access to the
        /// resolution process after the object is constructed but before
        /// the Resolve() (or its equivalent) call returns.
        /// </summary>
        public virtual void Container_can_provide_access_to_the_resolution_pipeline()
        {
            
        }

        /// <summary>
        /// Can the container be configured to use weak references to the
        /// objects it maintains.  Objects so maintained by the container 
        /// can be garbage collected when there are no remaining references 
        /// to the instance outside of the container.
        /// </summary>
        public virtual void Container_can_use_weak_references_to_resolved_instances()
        {
            
        }

        /// <summary>
        /// Can the container provide a reference to its service locator
        /// interface to fulfill a component's dependency?
        /// </summary>
        public virtual void Container_can_provide_a_reference_to_itself_as_a_dependency()
        {
            // e.g. Prism/Unity
        }

        public virtual void Configuration_scenario()
        {
            // e.g. Autofac Modules
        }

        protected Contact GetTestContact()
        {
            return new Contact {Name = new Name {FirstName = "Groucho", LastName = "Marx"}, Address = GetTestAddress(), Birthdate = new DateTime(1890, 10, 2)};
        }

        protected Address GetTestAddress()
        {
            return new Address {Address1 = "6851 Boxford Drive", City = "San Diego", State = "CA", Zip = "92117"};
        }

        protected static IEnumerable<Type> GetTypesFromAssembly(Type serviceType, Assembly targetAssembly)
        {
            return targetAssembly.GetTypes().Where(x => serviceType.IsAssignableFrom(x) && x != serviceType);
        }
    }
}